package loan.calc.config;

import loan.calc.constant.dict.base.BaseDictEnum;
import loan.calc.constant.dict.base.EnumDesc;
import loan.calc.constant.dict.base.EnumValue;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

public class BaseEnumConverter {

    private static final Map<String, String> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();
    private static final Map<String, Field> TABLE_METHOD_OF_ENUM_TYPES2 = new ConcurrentHashMap<>();
    private static final Map<String, String> TABLE_METHOD_OF_ENUM_DESC_FILED_NAME = new ConcurrentHashMap<>();
    private static final Map<String, Field> TABLE_METHOD_OF_ENUM_DESC_FILED = new ConcurrentHashMap<>();

    public static <E extends Enum<E>> String getValueFieldName(Class<E> enumClassType) {
        String name;
        if (!BaseDictEnum.class.isAssignableFrom(enumClassType)) {
            Optional<String> enumValueFieldName = findEnumValueFieldName(enumClassType);
            // 还没有，就直接用name();了
            name = enumValueFieldName.orElse("name");
        } else {
            name = "value";
        }
        return name;
    }

    public static <E extends Enum<E>> Field getValueField(Class<E> enumClassType) {
        Field field;
        if (!BaseDictEnum.class.isAssignableFrom(enumClassType)) {
            Optional<Field> enumValueField = findEnumValueAnnotationField(enumClassType);
            // 还没有，就直接用name了
            field = enumValueField.orElse(ReflectionUtils.findField(enumClassType, "name"));
        } else {
            field = ReflectionUtils.findField(enumClassType, "value");
        }
        return field;
    }

    public static <E extends Enum<E>> Field getDescField(Class<E> enumClassType) {
        Field field;
        if (!BaseDictEnum.class.isAssignableFrom(enumClassType)) {
            Optional<Field> enumDescAnnotationField = findEnumDescAnnotationField(enumClassType);
            field = enumDescAnnotationField.orElse(getValueField(enumClassType));
        } else {
            field = ReflectionUtils.findField(enumClassType, "description");
        }
        return field;
    }

    /**
     * 查找标记标记EnumValue字段
     *
     * @param clazz class
     * @return EnumValue字段
     */
    private static Optional<String> findEnumValueFieldName(Class<?> clazz) {
        if (clazz != null && clazz.isEnum()) {
            String className = clazz.getName();
            return Optional.ofNullable(TABLE_METHOD_OF_ENUM_TYPES.computeIfAbsent(className, key -> {
                Optional<Field> fieldOptional = findEnumValueAnnotationField(clazz);
                return fieldOptional.map(Field::getName).orElse(null);
            }));
        }
        return Optional.empty();
    }

    /**
     * 查找标记标记EnumDesc字段
     *
     * @param clazz class
     * @return EnumDesc字段
     */
    private static Optional<String> findEnumDescFieldName(Class<?> clazz) {
        if (clazz != null && clazz.isEnum()) {
            String className = clazz.getName();
            return Optional.ofNullable(TABLE_METHOD_OF_ENUM_DESC_FILED_NAME.computeIfAbsent(className, key -> {
                Optional<Field> fieldOptional = findEnumDescAnnotationField(clazz);
                return fieldOptional.map(Field::getName).orElse(null);
            }));
        }
        return Optional.empty();
    }

    public static Optional<Field> findEnumValueAnnotationField(Class<?> clazz) {
        String className = clazz.getName();
        return Optional.ofNullable(TABLE_METHOD_OF_ENUM_TYPES2.computeIfAbsent(className, key ->
                Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst().orElse(null)
        ));
    }

    public static Optional<Field> findEnumDescAnnotationField(Class<?> clazz) {
        String className = clazz.getName();
        return Optional.ofNullable(TABLE_METHOD_OF_ENUM_DESC_FILED.computeIfAbsent(className, key ->
                Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumDesc.class)).findFirst().orElse(null)
        ));
    }
}
